/*
 * AuthApplication.java
 *
 * Created on July 30, 2007, 5:00 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.auth.service.app;

import java.sql.SQLException;
import java.util.logging.Level;
import org.atomojo.auth.service.db.AuthDB;
import org.atomojo.auth.service.db.Group;
import org.atomojo.auth.service.db.Permission;
import org.atomojo.auth.service.db.RealmUser;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.ChallengeScheme;
import org.restlet.routing.Filter;
import org.restlet.routing.Router;
import org.restlet.routing.Template;

/**
 *
 * @author alex
 */
public class AuthApplication extends Application
{
   static String DB_ATTR = "org.atomoj.auth.db";
   static String REALM_ATTR = "org.atomoj.auth.realm";
   static String REALM_REQUIRED_ATTR = "org.atomoj.auth.realm.required";
   static String USER_ATTR = "org.atomoj.auth.user";
   
   class DBRouter extends Router {
      DBRouter(Context context) {
         super(context);
         setDefaultMatchingMode(Template.MODE_STARTS_WITH);
      }
      public void handle(Request request,Response response)
      {
         request.getAttributes().put(DB_ATTR,db);
         super.handle(request,response);
      }
   }

   AuthDB db;
   /** Creates a new instance of AuthApplication */
   public AuthApplication(Context context,AuthDB db)
   {
      super(context);
      getTunnelService().setEnabled(false);
      this.db = db;
   }
   
   public static String getStringAttribute(Request request,String name,String defaultValue)
   {
      Object v = request.getAttributes().get(name);
      return v==null ? defaultValue : v.toString();
   }
   
   @Override
   public Restlet createRoot() {   
      
      Permission canQuery = null;
      Permission superuser = null;
      Permission realmSuperuser = null;
      try {
         canQuery = db.getPermission(AuthDB.QUERY_PERMISSION);
         if (canQuery==null) {
            getContext().getLogger().severe("Cannot find query permission "+AuthDB.QUERY_PERMISSION);
            return null;
         }
         superuser = db.getPermission(AuthDB.SUPERUSER_PERMISSION);
         if (superuser==null) {
            getContext().getLogger().severe("Cannot find superuser permission "+AuthDB.SUPERUSER_PERMISSION);
            return null;
         }
         realmSuperuser = db.getPermission(AuthDB.REALM_SUPERUSER_PERMISSION);
         if (realmSuperuser==null) {
            getContext().getLogger().severe("Cannot find root role "+AuthDB.REALM_SUPERUSER_PERMISSION);
            return null;
         }
      } catch (SQLException ex) {
         getContext().getLogger().log(Level.SEVERE,"Cannot get query or root roles.",ex);
         return null;
      }
      
      Router mainRouter = new DBRouter(getContext());
      mainRouter.attach("/auth",AuthResource.class);
      mainRouter.attach("/auth/{session}",AuthResource.class);

      UserGuard queryGuard = new UserGuard(getContext(),db,ChallengeScheme.HTTP_BASIC,"users");
      queryGuard.setPermission(canQuery);
      mainRouter.attach("/query",queryGuard);
      
      Router queryRouter = new DBRouter(getContext());
      queryGuard.setNext(queryRouter);
      
      queryRouter.attach("/role/n/{name}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/role/n/{name}/n/{permission-name}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/role/n/{name}/{permission-id}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/role/{uuid}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/role/{uuid}/n/{permission-name}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/role/{uuid}/{permission-id}",RoleQueryResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/n/{name}/{user-id}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/n/{name}/a/{user-alias}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/n/{name}/realm/{realm-id}/{user-id}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/n/{name}/realm/n/{realm-name}/a/{user-alias}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/{uuid}/{user-id}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/{uuid}/a/{user-alias}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/{uuid}/realm/{realm-id}/{user-id}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      queryRouter.attach("/has/role/{uuid}/realm/n/{realm-name}/a/{user-alias}",RoleCheckResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);

      Router realmsRouter = new DBRouter(getContext());
      mainRouter.attach("/realms",realmsRouter);

      Router realmRouter = new Router(getContext());
      realmRouter.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
      
      realmRouter.attach("",RealmResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/",RealmResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/auth/recover/s/{session}",UserRecoveryResource.class);
      realmRouter.attach("/auth/{session}",AuthResource.class);
      realmRouter.attach("/auth",AuthResource.class);
      realmRouter.attach("/recover/a/{alias}",UserRecoveryResource.class);
      realmRouter.attach("/recover/{email}",UserRecoveryResource.class);
      realmRouter.attach("/recover/s/{session}",UserRecoveryResource.class);
      realmRouter.attach("/users",RealmUsersResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/",RealmUsersResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}/",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}/{facet}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}/{facet}/",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}/{facet}/{facet-id}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/a/{user-alias}/{facet}/a/{facet-name}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}/",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}/{facet}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}/{facet}/",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}/{facet}/{facet-id}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/users/{user-id}/{facet}/a/{facet-name}",RealmUserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups",GroupsResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/",GroupsResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/{facet}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/{facet}/",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/{facet}/{facet-id}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/{facet}/a/{facet-name}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/a/{group-alias}/{facet}/n/{facet-name}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/{facet}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/{facet}/",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/{facet}/{facet-id}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/{facet}/a/{facet-name}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmRouter.attach("/groups/{group-id}/{facet}/n/{facet-name}",GroupResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      
      Filter realmByName = new RealmFilter(getContext(),db);
      mainRouter.attach("/realms/n/{realm-name}",realmByName);
      RealmUserGuard realmByNameGuard = new RealmUserGuard(getContext(),db,null,ChallengeScheme.HTTP_BASIC,"Realm Users",superuser) {
         protected boolean hasRealmSpecific(Request request,RealmUser user)
         {
            // we need to check the group because a user could have realm superuser for another realm
            try {
               Group admin = db.getGroup(user.getRealm(),"admin");
               return user.isMemberOf(admin); 
            } catch (SQLException ex) {
               getContext().getLogger().log(Level.SEVERE,"Cannot check role due to database error.",ex);
               return false;
            }
         }
      };
      realmByNameGuard.addPermission(realmSuperuser);
      realmByName.setNext(realmByNameGuard);
      realmByNameGuard.setNext(realmRouter);
      
      Filter realmById = new RealmFilter(getContext(),db);
      mainRouter.attach("/realms/{realm-id}",realmById);
      RealmUserGuard realmByIdGuard = new RealmUserGuard(getContext(),db,null,ChallengeScheme.HTTP_BASIC,"Realm Users",superuser) {
         protected boolean hasRealmSpecific(Request request,RealmUser user)
         {
            try {
               Group admin = db.getGroup(user.getRealm(),"admin");
               return user.isMemberOf(admin); 
            } catch (SQLException ex) {
               getContext().getLogger().log(Level.SEVERE,"Cannot check role due to database error.",ex);
               return false;
            }
         }
      };
      realmByIdGuard.addPermission(realmSuperuser);
      realmById.setNext(realmByIdGuard);
      realmByIdGuard.setNext(realmRouter);
      
      UserGuard adminGuard = new UserGuard(getContext(),db,ChallengeScheme.HTTP_BASIC,"users");
      adminGuard.setPermission(superuser);
      mainRouter.attach("/admin",adminGuard);
      
      Router adminRouter = new DBRouter(getContext());
      adminGuard.setNext(adminRouter);
      
      adminRouter.attach("/backup",BackupResource.class);
      adminRouter.attach("/restore",RestoreResource.class);
      adminRouter.attach("/permissions/",PermissionsResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/permissions/{uuid}",PermissionResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/permissions/n/{name}",PermissionResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/",RolesResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/n/{name}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/n/{name}/",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/n/{name}/{pid}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/n/{name}/n/{pname}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/{uuid}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/{uuid}/",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/{uuid}/{pid}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/roles/{uuid}/n/{pname}",RoleResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/",UsersResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/all",AllUsersResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}/",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}/{facet}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}/{facet}/",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}/{facet}/{facet-id}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/a/{user-alias}/{facet}/n/{facet-name}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}/",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}/{facet}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}/{facet}/",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}/{facet}/{facet-id}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      adminRouter.attach("/users/{user-id}/{facet}/{facet-name}",UserResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      
      Router realmsAdminRouter = new Router(getContext());
      realmsAdminRouter.setDefaultMatchingMode(Template.MODE_STARTS_WITH);
      adminRouter.attach("/realms",realmsAdminRouter);
      
      realmsAdminRouter.attach("",RealmsResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmsAdminRouter.attach("/",RealmsResource.class).getTemplate().setMatchingMode(Template.MODE_EQUALS);
      realmsAdminRouter.attach("/n/{realm-name}",realmByName);
      realmsAdminRouter.attach("/{realm-id}",realmById);
      
      return mainRouter;
      
   }
}
